//////////////////////////////////////
// Fast Container Library           //
// MIT License - Copyright (c) 2012 //
//////////////////////////////////////

#pragma once

#include "common.h"
#include <cmath>

FC_NAMESPACE_BEGIN


template <class T> FC_FORCE_INLINE
	bool is_whitespace( T c )
	{
		return (c == T(' ') || c == T('\t'));
	}

template <class T> FC_FORCE_INLINE
	bool is_digit( T c )
	{
		return (c >= T('0') && c <= T('9'));
	}

template <class T> FC_FORCE_INLINE
	T to_lower(T c)
	{
		if ( (c >= 'A') && (c <= 'Z') )
			c = c + ('a' - 'A');
		return c;
	}

template <class T> FC_FORCE_INLINE
	T to_upper(T c)
	{
		if( (c >= 'a') && (c <= 'z') )
			c = c - ('a' - 'A');
		return c;
	}

template <class T> inline
	void make_upper( T* first, T* last )
	{
		FC_ASSERT(first <= last);
		for( ; first != last; ++first )
			*first = fc::to_upper(*first);
	}

template <class T> inline
	void make_lower( T* first, T* last )
	{
		FC_ASSERT(first <= last);
		for( ; first != last; ++first )
			*first = fc::to_lower(*first);
	}



inline int to_int( const char* source, char** endptr, int defaultValue = 0 )
	{
		int c, sign, n = 0;
		const char* digitptr;
		const char *p = (const char *)source;

		if( !source )
			return defaultValue;

		// bypass leading whitespace
		while( (*p == ' ' || *p == '\t') )
			++p;

		c = *p++;
		sign = c;
		if( c == '-' || c == '+' )
			c = *p++;

		digitptr = p;
		while( c >= '0' && c <= '9' )
		{
			n = 10 * n + (c - '0');
			c = *p++;
		}

		if( digitptr == p ) n = defaultValue; //invalid string
		else if (sign == '-') n = -n;

		if( endptr )
			*endptr = (char*)p;

		return n;
	}

inline int to_int( const char *p )
	{
		return to_int(p, (char**)0, 0);
	}


// to_double function based on fast atof code by Tom Van Baak
inline double to_double( const char* source, char** endptr, double defaultValue = 0.0 )
	{
		int frac;
		double sign, value, scale;
		const char *p = (const char *)source;

		if( !p )
			return defaultValue;

		// bypass leading whitespace
		while( *p == ' ' || *p == '\t' )
			++p;

		sign = 1.0;
		if( *p == '-' ) sign = -1.0, ++p;
		else if (*p == '+') ++p;

		const char* digitptr = p;
		for( value = 0.0; is_digit(*p); ++p )
			value = value * 10.0 + (*p - '0');

		if( *p == '.' )
		{
			double pow10 = 10.0;
			while( is_digit(*++p) )
			{
				value += (*p - '0') / pow10;
				pow10 *= 10.0;
			}
		}

		frac = 0;
		scale = 1.0;
		if( *p == 'e' || *p == 'E' )
		{
			unsigned int exponent;

			++p;
			if (*p == '-') frac = 1, ++p;
			else if (*p == '+') ++p;

			for( exponent = 0; is_digit(*p); ++p )
				exponent = exponent * 10 + (*p - '0');

			if( exponent > 308 ) exponent = 308;
			while (exponent >= 50) { scale *= 1E50; exponent -= 50; }
			while (exponent >=  8) { scale *= 1E8;  exponent -=  8; }
			while (exponent >   0) { scale *= 10.0; exponent -=  1; }
		}

		if( endptr )
			*endptr = (char*)p;

		if( digitptr == p )
			return defaultValue;

		return (sign * (frac ? (value / scale) : (value * scale)));
	}

inline double to_double( const char *p )
	{
		return to_double(p, (char**)0, 0.0);
	}

inline float to_float( const char *p, char** endptr, float defaultValue = 0.f )
	{
		return (float)to_double(p, endptr, (double)defaultValue);
	}

inline float to_float( const char *p )
	{
		return (float)to_double(p, (char**)0, 0.0);
	}

inline size_t to_string( size_t value, char* bufptr )
	{
		char buffer[16];
		char* p = buffer;

		do {
			*p++ = (value % 10) + '0';
			value /= 10;
		}
		while( value != 0 );

		char* begin = bufptr;
		while( p-- != buffer )
			*bufptr++ = *p;

		*bufptr = 0;
		return size_t(bufptr - begin);
	}

inline size_t to_string( int value, char* bufptr )
	{
		char* p = bufptr;
		if( value < 0 )
		{
			value = -value;
			*p++ = '-';
		}

		return to_string((size_t)value, p);
	}


inline size_t to_string( double value, char* bufptr )
	{
		char buffer[16];

		//nan
		if( value != value)
		{
			bufptr[0] = '0';
			bufptr[1] = 0;
			return (size_t)2;
		}

		int neg = 0;
		if( value < 0.0 )
			neg = 1, value = -value;

		int whole = (int)value;
		int frac = (int)((value - (double)whole) * 1000000.0);
		char* p = buffer;

		//exponent
		if( value > (float)(0x07FFFFFF) )
		{
			*p++ = 'e';
			*p++ = neg ? '-' : '+';

			int m = (int)log10(value);
			while( m > 0 )
			{
				*p++ = '0' + m % 10;
				m /= 10;
				m++;
			}
		}
		else
		{
			//decimal
			if( frac != 0 )
			{
				while( frac && !(frac % 10) )
					frac /= 10;

				do *p++ = (char)('0' + (frac % 10)); while (frac /= 10);
				*p++ = '.';
			}

			//whole
			do *p++ = (char)('0' + (whole % 10)); while (whole /= 10);
			if( neg ) *p++ = '-';
		}

		char* begin = bufptr;
		while( p-- != buffer )
			*bufptr++ = *p;

		*bufptr = 0;
		return (size_t)(bufptr - begin);
	}


inline size_t to_string( float value, char* bufptr )
	{
		return to_string((double)value, bufptr);
	}



FC_NAMESPACE_END

